//
// Created by pzw on 24-2-22.
//

#include "file_advance.h"

/**
 * 利用fcntl系统调用，你可以对打开的文件描述符执行各种操作，
 * 包括对它们进行复制、获取和设置文件描述符标志、获取和设置文件状态标志，以及管理建议性文件锁等；
 * 对不同操作的选择是通过选取命令参数cmd不同的值来实现的，其取值定义在头文件fcntl.h中；
 * 根据所选择的命令的不同，系统调用可能还需要第三个参数arg；
 *
 * @param fildes - 要对其进行操作的文件描述符
 * @param cmd    - 操作的命令
 * @return
 */
int file_advance::file_advance_fcntl(int fildes, int cmd) {
    return fcntl(fildes, cmd);
}

int file_advance::file_advance_fcntl(int fildes, int cmd, long arg) {
    return fcntl(fildes, cmd, arg);
}

/**
 * mmap(内存映射)函数的作用是建立一段可以被两个或者更多个程序读写的内存；
 * 一个程序对它所做出的修改，可以被其他程序看见；
 *
 * 这一功能还可以用在文件的处理上；你可以使得某个磁盘文件的全部内容看起来就像内存中的一个数组；
 * 如果文件由记录组成，而这些记录又能够用C语言中的结构来描述的话，你就可以通过访问结构数组来更新文件的内容了；
 *
 * mmap函数创建一个指向一段内存区域的指针，该内存区域与可以通过一个打开的文件描述符访问的文件的内容相关联；
 *
 * 你可以通过传递off参数来改变经共享内存段访问的文件中数据的起始偏移值；打开的文件描述
 * 符由fildes参数给出；可以访问的数据量(即内存段的长度)由len参数设置；
 * 你可以通过addr参数来请求使用某个特定的内存地址；如果它的取值是零，结果指针就将自动分配；这是推荐的做法，否则就会降低程序的可移植性；
 * prot参数用于设置内存段的访问权限；它是下列常数值的按位OR结果：
 * PROT_READ: 允许读取该内存段
 * PROT_WRITE: 允许写入该内存段
 * PROT_EXEC: 允许执行该内存段
 * PROT_NONE: 该内存段不能被访问
 *
 * @param addr    - 你可以通过addr参数来请求使用某个特定的内存地址；如果它的取值是零，
 *                  结果指针就将自动分配；这是推荐的做法，否则就会降低程序的可移植性；
 * @param len     - 可以访问的数据量(即内存段的长度)由len参数设置；
 * @param prot    - prot参数用于设置内存段的访问权限；它是下列常数值的按位OR结果：
 * @param flags   - flags参数控制程序对该内存段的改变所造成的影响；
 *                  MAP_PRIVATE: 内存段是私有的，对它的修改只对本进程有效
 *                  MAP_SHARED: 把对该内存段的修改保存到磁盘文件中
 *                  MAP_FIXED: 该内存段必须卫浴addr指定的地址处
 * @param fildes  - 打开的文件描述符由fildes参数给出
 * @param off     - off参数来改变经共享内存段访问的文件中数据的起始偏移值
 * @return void * -
 */
void *file_advance::file_advance_mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) {
    return mmap(addr, len, prot, flags, fildes, off);
}

int file_advance::file_advance_msync(void *addr, size_t len, int flags) {
    return msync(addr, len, flags);
}

int file_advance::file_advance_munmap(void *addr, size_t len) {
    return munmap(addr, len);
}

int file_advance_fcntl_main() {
    int fd;
    int newfd = 0;
    int flags = 0;

    fd = open("home/pzw/test", O_WRONLY);

    file_advance file_advance{};
    file_advance.file_advance_fcntl(fd, F_DUPFD, newfd);
    file_advance.file_advance_fcntl(fd, F_GETFD);
    file_advance.file_advance_fcntl(fd, F_SETFD, flags);
    file_advance.file_advance_fcntl(fd, F_GETFL);
    file_advance.file_advance_fcntl(fd, F_SETFL, flags);

    return 0;
}

#include "file_stdio.h"
#include "file_syscall.h"

typedef struct {
    int integer;
    char string[24];
} RECORD;

#define NRECORDS (100)

int file_advance_mmap_main() {
    file_stdio file_stdio{};
    file_syscall file_syscall{};
    file_advance file_advance{};

    RECORD record, *mapped;
    int i, f;
    FILE *fp;

    //(1) 我们先定义一个RECORD数据结构，然后创建出NRECORDS个记录，每个记录中保存着它们各自
    //的编号；然后把这些记录都追加到文件records.dat里去
    fp = file_stdio.file_stdio_fopen("records.dat", "w+");
    for (i = 0; i < NRECORDS; ++i) {
        record.integer = i;
        file_stdio.file_stdio_sprintf(record.string, "RECORD-%d", i);
        file_stdio.file_stdio_fwrite(&record, sizeof(record), 1, fp);
    }
    file_stdio.file_stdio_fclose(fp);

    //(2) 接着，我们把第43条记录中的整数值由43修改为143,并把它写入第43条记录中的字符串
    fp = fopen("records.dat", "r+");
    file_stdio.file_stdio_fseek(fp, 43 * sizeof(record), SEEK_SET);
    file_stdio.file_stdio_fread(&record, sizeof(record), 1, fp);
    record.integer = 143;
    file_stdio.file_stdio_sprintf(record.string, "RECORD-%d", record.integer);
    file_stdio.file_stdio_fseek(fp, 43 * sizeof(record), SEEK_SET);
    file_stdio.file_stdio_fwrite(&record, sizeof(record), 1, fp);
    file_stdio.file_stdio_fclose(fp);

    //(3)现在把这些记录映射到内存中，然后访问第43条记录，把它的整数值修改243(同时更新该
    //记录中的字符串)，使用的还是内存映射的方法
    f = file_syscall.file_sys_open("records.dat", O_RDWR);
    mapped = (RECORD *) file_advance.file_advance_mmap(0, NRECORDS * sizeof(record),
                                                       PROT_READ | PROT_WRITE, MAP_SHARED, f, 0);
    mapped[43].integer = 243;
    file_stdio.file_stdio_sprintf(mapped[43].string, "RECORD-%d", mapped[43].integer);
    file_advance.file_advance_msync((void *) mapped, NRECORDS * sizeof(record), MS_ASYNC);
    file_advance.file_advance_munmap((void *) mapped, NRECORDS * sizeof(record));
    file_syscall.file_sys_close(f);

    return 0;
}